home *** CD-ROM | disk | FTP | other *** search
/ Info-Mac 4 / Info_Mac IV CD-ROM (Pacific HiTech Inc.)(August 1994).iso / Development / General / SC++ 7.0.2 Update / TCL Demos / Art Class ƒ / Art Class Sources / CPaintPane.cp < prev    next >
Encoding:
Text File  |  1994-04-14  |  16.8 KB  |  627 lines  |  [TEXT/KAHL]

  1. /******************************************************************************
  2.  CPaintPane.c
  3.  
  4.                             The PaintPane Class
  5.                             
  6.     A MacPaint-type pane which stores a bitmap image. The PaintPane has
  7.     its own offscreen image and port. The "real" image is stored offscreen
  8.     and CopyBits operations are performed to put the image on the screen.
  9.     A single ScratchPad BitMap, the same size as the paint image, is used
  10.     in order to have smooth drawing (no flashing). This one scratchpad is
  11.     shared by all PaintPanes (i.e, multiple windows). To implement undo,
  12.     another bitmap is created to store sufficient information to restore
  13.     the state of a painting. Each PaintPane has it's own undo buffer.
  14.     With multiple windows, this means that the "Undo" command will undo
  15.     the last action for the active window.
  16.     
  17.     The drawing tools are pretty simple: geometric shapes, pencil, brush,
  18.     text tool, and selection rectangle.
  19.     
  20.     Geometric shapes are constrained to squares (or circles) if the
  21.     shift key is held down.
  22.     
  23.         
  24.     SUPERCLASS = CBitMapPane
  25.     
  26.     Copyright © 1989 Symantec Corporation. All rights reserved.
  27.     
  28.  ******************************************************************************/
  29.  
  30. #include "Global.h"
  31. #include "Commands.h"
  32. #include "CBartender.h"
  33. #include "CClipboard.h"
  34. #include "CPaintPane.h"
  35. #include "CPaintTask.h"
  36. #include "CToolShapes.h"
  37. #include "CToolPens.h"
  38. #include "CDragger.h"
  39. #include "CSelectionRect.h"
  40. #include "CToolText.h"
  41. #include "CCaption.h"                          // TCL 2.0 2/14/94 KI
  42. #include "CClearTask.h"
  43. #include "ArtClassCmds.h"
  44. #include "CPNTGFile.h"
  45.  
  46. /*** Global Variables ***/
  47.  
  48. extern CBartender    *gBartender;        /* Manages all menus                */
  49. extern CClipboard    *gClipboard;        /* Copies and Pastes data            */
  50. extern CBureaucrat    *gGopher;            /* First in line to get commands    */
  51. extern RgnHandle    gUtilRgn;            /* Utility region                    */
  52. extern long            gSleepTime;            /* Max time between events            */
  53.  
  54. extern short        gFontNum;            /* Font number                        */
  55. extern short        gFontSize;            /* Font size                        */
  56. extern Style        gFontStyle;            /* Current font style                */
  57. extern long            gLineSpacing;        /* Line spacing cmd number            */
  58.  
  59. extern short            gPaintTool;
  60. extern CursHandle        gShapeCurs;
  61. extern CursHandle        gPencilCurs;
  62. extern CursHandle        gEraserCurs;
  63. extern CursHandle        gBrushCurs;
  64. extern CursHandle        gDragCurs;
  65. extern CursHandle        gSelectCurs;
  66. extern CursHandle        gIBeamCursor;
  67. extern CBitMap            *gScratchPad;
  68.  
  69. #define        DELETE_KEY        8            /* Character code                    */
  70. #define        PAT_MARCH_ANTS    300            /* Pattern for marching ants        */
  71. #define        iCUT            2            /* Index of task name for Cut        */
  72. #define        iCLEAR            5            /* Index of task name for Clear        */
  73.  
  74. /**** C O N S T R U C T I O N / D E S T R U C T I O N   M E T H O D S ****/
  75.  
  76.  
  77. /******************************************************************************
  78.  IPaintPane
  79.  
  80.         Initialize a PaintPane object
  81.  ******************************************************************************/
  82.  
  83. CPaintPane::CPaintPane(
  84.     CView            *anEnclosure,
  85.     CBureaucrat        *aSupervisor,
  86.     short            aWidth,
  87.     short            aHeight,
  88.     short            aHEncl,
  89.     short            aVEncl,
  90.     SizingOption    aHSizing,
  91.     SizingOption    aVSizing,
  92.     LongRect        *aBounds,
  93.     CBitMap            *aBitMap)
  94.     : CBitMapPane(anEnclosure, aSupervisor,
  95.                   aWidth, aHeight, aHEncl, aVEncl, aHSizing, aVSizing,
  96.                   aBounds, aBitMap, TRUE)
  97. {    
  98.     wantsClicks = TRUE;
  99.     SetLongRect(&selRect, 0, 0, 0, 0);
  100.     itsCaption  = NULL;                   // TCL 2.0 2/14/94 KI
  101. }
  102.  
  103. void    CPaintPane::StartCaption(LongPt    *startPt)
  104. {
  105.                                         /* Create a Caption Pane to handle    */
  106.                                         /*   keystrokes and text display    */
  107.                                         
  108.     TextFont(gFontNum);                    /* Set font characteristics            */
  109.     TextSize(gFontSize);
  110.     TextFace(gFontStyle);                                    
  111.                                         /* Note how the PaintPane is both    */
  112.                                         /*   the enclosure and supervisor    */
  113.                                         /*   of the Caption. This is        */
  114.                                         /*   important because we want the    */
  115.                                         /*   PaintPane to become the Gopher    */
  116.                                         /*   again when we get rid of the    */
  117.                                         /*   Caption pane.
  118.                                                             */
  119.     itsCaption = new CCaption(this,this,PNTG_WIDTH,startPt->h,startPt->v);
  120.                             
  121.     itsCaption->SetSpacingCmd(gLineSpacing);
  122.     itsCaption->Activate();
  123.     itsCaption->BecomeGopher( TRUE);
  124. }
  125. /******************************************************************************
  126.  DoCommand {OVERRIDE}
  127.  
  128.         Handle Edit commands
  129.  ******************************************************************************/
  130.  
  131. void    CPaintPane::DoCommand(
  132.     long        theCommand)
  133. {
  134.     if (HiShort(-theCommand) == MENUtools) {
  135.         KillSelRect();
  136.         CBitMapPane::DoCommand(theCommand);
  137.     
  138.     } else {
  139.         switch (theCommand) {
  140.         
  141.             case cmdCut:
  142.                 DoCopy();
  143.                 DoClear(iCUT);
  144.                 break;
  145.                 
  146.             case cmdCopy:
  147.                 DoCopy();
  148.                 break;
  149.                 
  150.             case cmdPaste:
  151.                 DoPaste();
  152.                 break;
  153.                 
  154.             case cmdClear:
  155.                 DoClear(iCLEAR);
  156.                 break;
  157.             
  158.             default:
  159.                 CBitMapPane::DoCommand(theCommand);
  160.                 break;
  161.         }
  162.     }
  163. }
  164.  
  165.  
  166. /******************************************************************************
  167.  UpdateMenus {OVERRIDE}
  168.  
  169.         Enable appropriate Edit commands
  170.  ******************************************************************************/
  171.  
  172. void    CPaintPane::UpdateMenus()
  173. {
  174.     CBitMapPane::UpdateMenus();
  175.  
  176.     if (gGopher == this) {
  177.         if (!EmptyLongRect(&selRect)) {
  178.             gBartender->EnableCmd(cmdCut);
  179.             gBartender->EnableCmd(cmdCopy);
  180.             gBartender->EnableCmd(cmdClear);
  181.         }
  182.         
  183.         if (gClipboard->DataSize('PICT') > 0) {
  184.             gBartender->EnableCmd(cmdPaste);
  185.         }
  186.     }
  187. }
  188.  
  189.  
  190. /******************************************************************************
  191.  DoClick {OVERRIDE}
  192.  
  193.         Mouse click inside the PaintPane
  194.  ******************************************************************************/
  195.  
  196. void    CPaintPane::DoClick(
  197.     Point        hitPt,
  198.     short        modifierKeys,
  199.     long        when)
  200. {
  201.     CPaintTask        *thePaintTask;
  202.     Rect            bounds;
  203.     Boolean            select = FALSE;
  204.     Boolean            notifiable = TRUE;
  205.     LongPt            trackStart;
  206.  
  207.     LongToQDRect( &this->bounds, &bounds);
  208.     QDToLongPt( hitPt, &trackStart);
  209.  
  210.     if (!PtInRect(hitPt, &bounds)) {
  211.         return;
  212.     }
  213.     
  214.     if (gPaintTool != toolSELECT) {    /* Any new painting will clobber the    */
  215.         KillSelRect();                /*   selection                            */
  216.     }
  217.  
  218.     switch (gPaintTool) {
  219.     
  220.         case toolSELECT:            /* We could be selecting or dragging    */
  221.             notifiable = FALSE;
  222.             if (PtInLongRect( &trackStart, &selRect)) {
  223.                                     /* Dragging the current selection        */
  224.                 if ((itsLastTask == NULL) || !member(itsLastTask, CDragger)) {
  225.                                     /* This is a brand new drag, NOT the    */
  226.                                     /*   continuation of a previous one        */
  227.                     itsLastTask = new CDragger(this, itsBitMap);
  228.                     notifiable = TRUE;
  229.                 }
  230.                     
  231.                 thePaintTask = (CPaintTask*) itsLastTask;
  232.                 
  233.             } else {                /* Make a selection rectangle            */
  234.                 select = TRUE;
  235.                 KillSelRect();
  236.                 thePaintTask = new CSelectionRect(this,itsBitMap);
  237.                 gSleepTime = 0;        /* Cursor shape needs to change within    */
  238.                                     /*   selection, so we must force an        */
  239.                                     /*   idle so that the mouse region will    */
  240.                                     /*   be updated                            */
  241.             }
  242.             break;
  243.             
  244.         case toolBRUSH:
  245.             thePaintTask = new CToolBrush(this, itsBitMap);
  246.             break;
  247.             
  248.         case toolPENCIL:
  249.             thePaintTask = new CToolPencil(this, itsBitMap);
  250.             break;
  251.             
  252.         case toolERASER:
  253.             thePaintTask = new CToolEraser(this, itsBitMap);
  254.             break;
  255.             
  256.         case toolRECT:
  257.             thePaintTask = new CToolRect(this, itsBitMap);
  258.             break;
  259.             
  260.         case toolFILLRECT:
  261.             thePaintTask = new CToolFillRect(this, itsBitMap);
  262.             break;
  263.             
  264.         case toolTEXT:
  265.             notifiable = FALSE;
  266.             thePaintTask = new CToolText(this, itsBitMap);
  267.             break;
  268.             
  269.         case toolRRECT:
  270.             thePaintTask = new CToolRRect(this, itsBitMap);
  271.             break;
  272.             
  273.         case toolFILLRRECT:
  274.             thePaintTask = new CToolFillRRect(this, itsBitMap);
  275.             break;
  276.             
  277.         case toolLINE:
  278.             thePaintTask = new CToolLine(this, itsBitMap);
  279.             break;
  280.             
  281.         case toolOVAL:
  282.             thePaintTask = new CToolOval(this, itsBitMap);
  283.             break;
  284.             
  285.         case toolFILLOVAL:
  286.             thePaintTask = new CToolFillOval(this, itsBitMap);
  287.             break;
  288.     }
  289.     
  290.                                     /* Mouse down drawing within the    */
  291.                                     /*   bounds of the painting            */
  292.     ForceNextPrepare();
  293.     Prepare();
  294.     TrackMouse(thePaintTask, &trackStart, &this->bounds);
  295.     
  296.     if (select) {                    /* Selection is not undoable so we    */
  297.         delete thePaintTask;        /*   don't need the task anymore    */
  298.     } else {
  299.         if (notifiable) {            /* Make sure we don't re-notify in    */
  300.                                     /*   the case of a resumed drag        */
  301.             itsSupervisor->Notify(thePaintTask);
  302.         }
  303.         itsLastTask = thePaintTask;    /* Save task for later reference    */
  304.     }
  305. }
  306.     
  307.  
  308. /******************************************************************************
  309.  AdjustCursor {OVERRIDE}
  310.  
  311.         Adjust the shape of the cursor according the position of the mouse.
  312.  ******************************************************************************/
  313.  
  314. void    CPaintPane::AdjustCursor(
  315.     Point        where,                    /* Mouse location in Window coords    */
  316.     RgnHandle    mouseRgn)                /* Mouse region in Global coords    */
  317. {
  318.     Rect        mouseRect;
  319.     LongPt        mouseLoc;
  320.  
  321.     WindToFrame(where, &mouseLoc);
  322.     if (!PtInLongRect( &mouseLoc, &this->bounds)) {
  323.         SetCursor(&qd.arrow);                /* Mouse is outside the drawing        */
  324.                                     /*   area                            */
  325.         FrameToGlobalR( &bounds, &mouseRect);
  326.         RectRgn(gUtilRgn, &mouseRect);
  327.         DiffRgn(mouseRgn, gUtilRgn, mouseRgn);
  328.         return;
  329.     }
  330.     
  331.     switch (gPaintTool) {                /* Cursor shape depends on tool        */
  332.     
  333.         case toolSELECT:
  334.             if (PtInLongRect( &mouseLoc, &selRect)) {
  335.                 SetCursor(*gDragCurs);
  336.             } else {
  337.                 SetCursor(*gSelectCurs);
  338.             }
  339.             break;
  340.             
  341.         case toolBRUSH:
  342.             SetCursor(*gBrushCurs);
  343.             break;
  344.             
  345.         case toolPENCIL:
  346.             SetCursor(*gPencilCurs);
  347.             break;
  348.             
  349.         case toolERASER:
  350.             SetCursor(*gEraserCurs);
  351.             break;
  352.             
  353.         case toolTEXT:
  354.             SetCursor(*gIBeamCursor);
  355.             break;
  356.             
  357.         case toolRECT:
  358.         case toolFILLRECT:
  359.         case toolRRECT:
  360.         case toolFILLRRECT:
  361.         case toolLINE:
  362.         case toolOVAL:
  363.         case toolFILLOVAL:
  364.             SetCursor(*gShapeCurs);
  365.             break;
  366.     }
  367.     
  368.     if (!EmptyLongRect(&selRect)) {            /* Cursor changes shape inside        */
  369.                                         /*   selection rectangle            */
  370.         FrameToGlobalR( &selRect, &mouseRect);
  371.         RectRgn(gUtilRgn, &mouseRect);
  372.         if (PtInLongRect( &mouseLoc, &selRect)) {
  373.             SectRgn(mouseRgn, gUtilRgn, mouseRgn);
  374.         } else {
  375.             DiffRgn(mouseRgn, gUtilRgn, mouseRgn);
  376.         }
  377.     }
  378.                                             /* In case frame is bigger than        */
  379.     FrameToGlobalR( &bounds, &mouseRect);    /*   bounds                            */
  380.     RectRgn(gUtilRgn, &mouseRect);
  381.     SectRgn(mouseRgn, gUtilRgn, mouseRgn);
  382. }
  383.     
  384.  
  385. /******************************************************************************
  386.  DoKeyDown {OVERRIDE}
  387.  
  388.         Hitting the backspace/delete key clears the current selection
  389.  ******************************************************************************/
  390.  
  391. void    CPaintPane::DoKeyDown(
  392.     char        theChar,
  393.     Byte        keyCode,
  394.     EventRecord    *macEvent)
  395. {
  396.     if ((theChar == DELETE_KEY)  &&  !EmptyLongRect(&selRect)) {
  397.         DoClear(iCLEAR);
  398.     }    
  399.     else
  400.         CBitMapPane::DoKeyDown(theChar, keyCode, macEvent);
  401. }
  402.  
  403.  
  404. /******************************************************************************
  405.  Dawdle {OVERRIDE}
  406.  
  407.         Animate the selection rectangle during Idle time
  408.  ******************************************************************************/
  409.  
  410. void    CPaintPane::Dawdle(
  411.     long    *maxSleep)
  412. {
  413.     Pattern        thePat;
  414.     long        ticks;
  415.     Rect        sRect;
  416.     
  417.     if (!EmptyLongRect(&selRect)) {        /* Make sure selection exists            */
  418.         LongToQDRect( &selRect, &sRect);
  419.         Prepare();
  420.         
  421.         PenNormal();                /* Draw rectangle with one pattern        */
  422.         #ifndef TCL_UNIVERSAL_HEADERS              // KMI 3/10/94
  423.           GetIndPattern(thePat, PAT_MARCH_ANTS, 1);
  424.         #else
  425.           GetIndPattern(&thePat, PAT_MARCH_ANTS, 1);
  426.         #endif
  427.         PenPat(&thePat);
  428.         FrameRect(&sRect);
  429.         
  430.         Delay(6, &ticks);            /* Wait a while                            */
  431.             
  432.                                     /* Redraw with a pattern offset from    */
  433.                                     /*   the first. This gives the            */
  434.                                     /*   illusion of marching ants.            */
  435.         #ifndef TCL_UNIVERSAL_HEADERS      // KMI 3/10/94
  436.           GetIndPattern(thePat, PAT_MARCH_ANTS, 2);
  437.         #else
  438.           GetIndPattern(&thePat, PAT_MARCH_ANTS, 2);
  439.         #endif
  440.         PenPat(&thePat);
  441.         FrameRect(&sRect);
  442.         *maxSleep = 5;
  443.     }
  444. }
  445.     
  446.  
  447. /******************************************************************************
  448.  ChangeSize
  449.  
  450.         Change the size of a PaintPane. If necessary, shift painting so
  451.         that it always completely covers the frame.
  452.  ******************************************************************************/
  453.  
  454. void    CPaintPane::ChangeSize(
  455.     register Rect        *delta,            /* Movement for each side            */
  456.     Boolean                redraw)            /* Redraw Pane or not?                */
  457. {
  458.     short        hShift;
  459.     short        vShift;
  460.     
  461.                                         /* If new size would cause the        */
  462.                                         /*   frame to extend beyond the        */
  463.                                         /*   bounds of the painting, we        */
  464.                                         /*   have to shift the painting.    */
  465.     hShift = Max(frame.right + delta->right - bounds.right, 0);
  466.     vShift = Max(frame.bottom + delta->bottom - bounds.bottom, 0);
  467.  
  468.     CBitMapPane::ChangeSize(delta, redraw);
  469.     
  470.     if ( (hShift > 0)  ||  (vShift > 0) ) {
  471.         Scroll(-hShift, -vShift, false);
  472.         if (redraw) {
  473.             Refresh();
  474.         }
  475.     }
  476. }
  477.  
  478.  
  479. /******************************************************************************
  480.  KillSelRect
  481.  
  482.         Get rid of selection rectangle and finalize the last task
  483.  ******************************************************************************/
  484.  
  485. void    CPaintPane::KillSelRect()
  486. {
  487.     Rect        sRect;
  488.     
  489.     if (itsLastTask != NULL) {        /* Killing selection means we are about    */
  490.         itsLastTask->Do();                /*   to do something new. Let the last    */
  491.         Notify( NULL);                /*   task finalize its actions.            */
  492.         itsLastTask = NULL;            
  493.     }
  494.     if (itsCaption != NULL)
  495.     {
  496.      PaintCaption();                // TCL 2.0 2/16/94
  497.      NotifyClean(NULL);
  498.     } 
  499.     FrameToWindR( &selRect, &sRect);
  500.                                     /* Set empty selection and force redraw    */
  501.     SetLongRect(&selRect, 0, 0, 0, 0);    /*   of old selection to clear out the    */
  502.     DrawAll(&sRect);                /*   marching ants                        */
  503. }
  504.     
  505.  
  506.  
  507. /******************************************************************************
  508.  DoCopy
  509.  
  510.         Copy selection to the clipboard. This action is NOT undoable.
  511.  ******************************************************************************/
  512.  
  513. void    CPaintPane::DoCopy()
  514. {
  515.     PicHandle        thePic;
  516.     LongRect        selRect;
  517.     Rect            qdRect;
  518.     
  519.     Prepare();
  520.     itsBitMap->BeginDrawing();
  521.     
  522.     FrameToQDR( &this->selRect, &qdRect);
  523.                                     /* Open picture to store selection    */
  524.     thePic = OpenPicture( &qdRect);
  525.                                     /* Draw selection on top of itself    */
  526.     selRect = this->selRect;
  527.     itsBitMap->CopyFrom( &selRect, &selRect, NULL);
  528.     ClosePicture();
  529.     itsBitMap->EndDrawing();
  530.                                     /* Copy picture to the clipboard    */
  531.     gClipboard->EmptyScrap();
  532.     gClipboard->PutData('PICT', (Handle)thePic);
  533.     KillPicture(thePic);
  534. }
  535.     
  536.  
  537. /******************************************************************************
  538.  DoPaste
  539.  
  540.         Paste picture from the clipboard. This action isn't truly
  541.         undoable. We fool the pane into thinking that a drag has
  542.         occurred.
  543.  ******************************************************************************/
  544.  
  545. void    CPaintPane::DoPaste()
  546. {
  547.     PicHandle        thePic;
  548.     LongPt            anyPt;
  549.     Rect            pFrame;
  550.     
  551.     if (gClipboard->GetData('PICT', (Handle*) &thePic)) {
  552.                                         /* Set tool to selection rectangle    */
  553.                                         /*   so pasted image can be dragged    */
  554.         DoCommand(-( (((long)MENUtools) << 16) + toolSELECT));
  555.                                         /* Paste is treated as a drag in    */
  556.         itsLastTask = new CDragger(this, itsBitMap);
  557.         
  558.                                         /* Place pasted picture near the    */
  559.                                         /*   top left of the pane            */
  560.         QDToLongRect( &(**thePic).picFrame, &selRect);
  561.         OffsetLongRect(&selRect, frame.left + 36 - selRect.left,
  562.                             frame.top + 36 - selRect.top);
  563.         
  564.                                         /* Save bits under the paste        */
  565.         LongToQDRect( &selRect, &pFrame);
  566.         CopyBits(itsBitMap->macBitMap, gScratchPad->macBitMap,
  567.                     &pFrame, &pFrame, srcCopy, NULL);
  568.             
  569.         itsBitMap->BeginDrawing();        /* Draw pasted image on painting    */
  570.         DrawPicture(thePic, &pFrame);
  571.         itsBitMap->EndDrawing();
  572.         
  573.         anyPt = topLeftL( selRect);    /* Fake out Dragger into thinking    */
  574.                                     /*   a selection has been dragged    */
  575.         ((CDragger*)itsLastTask)->BeginTracking( &anyPt);
  576.         
  577.         CopyBits(gScratchPad->macBitMap, itsBitMap->macBitMap,
  578.                     &pFrame, &pFrame, srcCopy, NULL);
  579.         
  580.         ((CDragger*)itsLastTask)->EndTracking(&anyPt, &anyPt, &anyPt);
  581.         
  582.         RefreshRect(&pFrame);            /* Force redraw and proceed as        */
  583.         gSleepTime = 0;                    /*   a suspended drag                */
  584.         itsSupervisor->Notify(itsLastTask);
  585.         KillPicture(thePic);
  586.     }
  587. }    
  588.  
  589.  
  590. /******************************************************************************
  591.  DoClear
  592.  
  593.         Erase the current selection
  594.  ******************************************************************************/
  595.  
  596. void    CPaintPane::DoClear(
  597.     short        taskIndex)
  598. {
  599.     register CClearTask    *theClear;
  600.                                         /* Can't get much simpler:            */
  601.                                         /*   Create a task, do it, and tell    */
  602.                                         /*   supervisor it's been done        */
  603.     theClear = new CClearTask(taskIndex, this, itsBitMap);
  604.     theClear->Do();
  605.     itsSupervisor->Notify(theClear);
  606.     itsLastTask = theClear;
  607. }
  608.  
  609. /******************************************************************************
  610.  PaintCaption        TCL 2.0 2/16/94 KI
  611.  
  612.         Finish off Text
  613.  ******************************************************************************/
  614.  
  615. void CPaintPane::PaintCaption()
  616. {
  617.     LongRect    tFrame;
  618.     
  619.     itsCaption->Deactivate();
  620.     itsCaption->FitToText ();
  621.     itsCaption->DrawInPort(itsBitMap->macPort);
  622.  
  623.     delete itsCaption;
  624.     itsCaption = NULL;
  625.     
  626.     BecomeGopher(TRUE);
  627. }